from numpy import *


def loadData(filename):
    # Python  strip()方法用于移除字符串头尾指定的字符（默认为空格或换行符）或字符序列
    # Python  split(str，num)通过指定分隔符对字符串进行切片，如果参数num有指定值，则仅分隔num个子字符串
    fr = open(filename)
    dataList = [line.strip().split('\t') for line in fr.readlines()]
    dataclean = []
    counttemp = zeros((590, 1))
    for line in dataList:
        # float(line)
        templine = line[0].strip().split(' ')
        templist = []
        for i in range(len(templine)):
            if templine[i] == 'NaN':
                tempnum = counttemp[i] / i
                templist.append(tempnum)
            else:
                tempnum = float(templine[i])
                counttemp[i] += tempnum
                templist.append(tempnum)
        dataclean.append(templist)
    return mat(dataclean)


# def cleandata(datMat):
#     numFeat = shape(dataArr)[1]
#     for i in range(numFeat):
#     # .A 返回矩阵基于的数组
#     # ~:按位取反运算符
#     meanVal = mean(datMat[nonzero(~isnan(datMat[:, i].A))[0], i])
#     # 将value为NaN的值赋值为均值
#     datMat[nonzero(isnan(datMat[:, i].A))[0], i] = meanVal
#     return datMat


# 解析文本数据函数
# @filename 文件名txt
# @delim 每一行不同特征数据之间的分隔方式，默认是tab键'\t'
# def loadDataSet(filename, delim='\t'):
#     # 打开文本文件
#     fr = open(filename)
#     # 对文本中每一行的特征分隔开来，存入列表中，作为列表的某一行
#     # 行中的每一列对应各个分隔开的特征
#     stringArr = [line.strip().split(delim) for line in fr.readlines()]
#     # 利用map()函数，将列表中每一行的数据值映射为float型
#     datArr = []


# for line in stringArr:
#     listtemp = list(map(float, line))
#     datArr.append(listtemp)
#     # 将float型数据值的列表转化为矩阵返回
#     return mat(datArr)


# 去除平均值
# 计算协方差矩阵
# 计算协方差矩阵的特征值和特征向量
# 将特征值排序
# 保留前N个最大的特征值对应的特征向量
# 将数据转换到上面得到的N个特征向量构建的新空间中（实现了特征压缩）
def pca(dataMat, topNfeat=100):
    # 求数据矩阵每一列的均值
    meanVals = mean(dataMat, axis=0)
    # 数据矩阵每一列特征减去该列的特征均值
    meanRemoved = dataMat - meanVals
    # 计算协方差矩阵，除数n-1是为了得到协方差的无偏估计
    # cov(X,0) = cov(X) 除数是n-1(n为样本个数)
    # cov(X,1) 除数是n
    # 已经除好了m
    covMat = cov(meanRemoved, rowvar=0)
    # 计算协方差矩阵的特征值及对应的特征向量
    # 均保存在相应的矩阵中
    eigVals, eigVects = linalg.eig(mat(covMat))
    # sort():对特征值矩阵排序(由小到大)
    # argsort():对特征值矩阵进行由小到大排序，返回对应排序后的索引
    # 只是作者为了秀一下，并没有什么卵用，花里胡哨的，不如从大到小排列
    eigValInd = argsort(eigVals)
    # 从排序后的矩阵最后一个开始自下而上选取最大的N个特征值，返回其对应的索引
    eigValInd = eigValInd[:-(topNfeat + 1):-1]
    # 将特征值最大的N个特征值对应索引的特征向量提取出来，组成压缩矩阵
    redEigVects = eigVects[:, eigValInd]
    # 将去除均值后的数据矩阵*压缩矩阵，转换到新的空间，使维度降低为N
    lowDDataMat = meanRemoved * redEigVects
    # 利用降维后的矩阵反构出原数据矩阵(用作测试，可跟未压缩的原矩阵比对)
    reconMat = (lowDDataMat * redEigVects.T) + meanVals
    # 返回压缩后的数据矩阵即该矩阵反构出原始数据矩阵
    return lowDDataMat, reconMat


if __name__ == '__main__':
    dataclean = loadData('C:/code/database/pcadata.txt')
    lowDDataMat, reconMat = pca(dataclean, topNfeat=5)
    print(shape(lowDDataMat))
    print(shape(reconMat))
